package net.ericaro.neojavagengen;

import java.util.ArrayList;
import java.util.List;

import net.ericaro.neojavagen.Literals;
import net.ericaro.neojavagen.blocks.Block;
import net.ericaro.neojavagen.blocks.BlockStatements;
import net.ericaro.neojavagen.blocks.ExpressionStatement;
import net.ericaro.neojavagen.blocks.ReturnStatement;
import net.ericaro.neojavagen.blocks.StatementExpression;
import net.ericaro.neojavagen.classes.ClassBodyDeclarations;
import net.ericaro.neojavagen.classes.ClassModifier;
import net.ericaro.neojavagen.classes.ClassModifiers;
import net.ericaro.neojavagen.classes.ConstructorBody;
import net.ericaro.neojavagen.classes.ConstructorDeclaration;
import net.ericaro.neojavagen.classes.ConstructorDeclarator;
import net.ericaro.neojavagen.classes.EnumBody;
import net.ericaro.neojavagen.classes.EnumBodyDeclarations;
import net.ericaro.neojavagen.classes.EnumConstant;
import net.ericaro.neojavagen.classes.EnumConstants;
import net.ericaro.neojavagen.classes.EnumDeclaration;
import net.ericaro.neojavagen.classes.FieldDeclaration;
import net.ericaro.neojavagen.classes.FormalParameter;
import net.ericaro.neojavagen.classes.FormalParameters;
import net.ericaro.neojavagen.classes.InterfaceTypes;
import net.ericaro.neojavagen.classes.Interfaces;
import net.ericaro.neojavagen.classes.MethodDeclaration;
import net.ericaro.neojavagen.classes.MethodDeclarator;
import net.ericaro.neojavagen.classes.MethodHeader;
import net.ericaro.neojavagen.classes.MethodModifier;
import net.ericaro.neojavagen.classes.MethodModifiers;
import net.ericaro.neojavagen.classes.ReturnMethodDeclarator;
import net.ericaro.neojavagen.classes.SimpleVariableDeclaratorId;
import net.ericaro.neojavagen.classes.VariableDeclaratorId;
import net.ericaro.neojavagen.classes.VariableDeclarators;
import net.ericaro.neojavagen.expressions.Arguments;
import net.ericaro.neojavagen.expressions.Assignment;
import net.ericaro.neojavagen.expressions.AssignmentOperator;
import net.ericaro.neojavagen.expressions.PrimaryAccess;
import net.ericaro.neojavagen.expressions.This;
import net.ericaro.neojavagen.interfaces.ExtendsInterfaces;
import net.ericaro.neojavagen.packages.TypeDeclaration;
import net.ericaro.neojavagen.types.ClassOrInterfaceType;
import net.ericaro.neojavagen.types.Identifier;

public class MetaEnum extends Meta{

	List<String> values = new ArrayList<String>();
	
	public MetaEnum(Grammar g, String name) {
		super(g, name);
	}
	
	public MetaEnum withLiteral(String value) {
		values.add(value);
		return this;
	}
	
	@Override
	protected TypeDeclaration getTypeDeclaration() {
		EnumConstants enumConstants = new EnumConstants();
		// fill constants
		
		for( String v : values){
			enumConstants.add(createEnumConstant(v));
		}
		
		// workaround, type declaration sadly handles null interfaces, but not empty ones (it generates an "implements" followed by nothing
		InterfaceTypes interfaceTypes = getMetaType().getInterfaceTypes();
		Interfaces interfaces =null ;
		if (interfaceTypes.size() > 0 ) 
			interfaces = new Interfaces(interfaceTypes);

		
		// add body declaration
		ClassBodyDeclarations bodyDeclarations = new ClassBodyDeclarations();
		
		Identifier symbolId = Literals.identifier("symbol");
		
		ClassOrInterfaceType symbolType = Grammar.fromJava(String.class);
		bodyDeclarations.add(
				new FieldDeclaration(
						symbolType, 
						new VariableDeclarators(new SimpleVariableDeclaratorId(new VariableDeclaratorId(symbolId)))));
		
		bodyDeclarations.add(
				new ConstructorDeclaration(new ConstructorDeclarator(null,getMetaType().getNameIdentifier(), new FormalParameters(new FormalParameter(symbolType, new VariableDeclaratorId(symbolId)))), new ConstructorBody(null,new BlockStatements(new ExpressionStatement(createAssignFieldStatement(symbolId)))))
				);
		
		// generate tostring
		BlockStatements blockstatements = new BlockStatements();
		bodyDeclarations.add(
				new MethodDeclaration(new MethodHeader(null,null,new MethodModifiers(MethodModifier.PUBLIC),null,new ReturnMethodDeclarator(Grammar.fromJava(String.class), new MethodDeclarator(Literals.identifier("toString")))  ,null ), new Block(blockstatements) )
		);
		blockstatements.add(new ReturnStatement(Grammar.unaryExpression(symbolId)));
		
		
		
		
		
		
		return new EnumDeclaration(null, new ClassModifiers(ClassModifier.PUBLIC), getMetaType().getNameIdentifier(), interfaces,  new EnumBody(enumConstants, true, new EnumBodyDeclarations(bodyDeclarations)) );
	}

	private StatementExpression createAssignFieldStatement(Identifier symbolId) {
		// generates a this.symbol = symbol
		return new Assignment(new PrimaryAccess(This.THIS, symbolId), AssignmentOperator.equalsSymbol, Grammar.unaryExpression(symbolId));
	}

	/** turn a string literal into an enum constant, convert the name, and passes the value as argument
	 * 
	 * @param v
	 * @return
	 */
	private EnumConstant createEnumConstant(String v) {
		return new EnumConstant(null, Grammar.nameSymbol(v.toUpperCase()), new Arguments(Grammar.unaryExpression(Literals.stringLiteral(v))), null);
	}

}
